﻿#include "ScriptRunner.h"

#include "ScriptInstruction.h"
#include "TurnTableCodec.h"
#include "RegisterCenter_Device.h"
#include "TransmitCodec.h"

ScriptRunner::ScriptRunner()
{
  qRegisterMetaType<QVector<QVector<ScriptInstruction*>>>("QVector<QVector<ScriptInstruction*>>");
}
ScriptRunner::~ScriptRunner()
{
  for(ScriptRunner1* scriptRunner1:deviceScriptList.values())
    {
      delete scriptRunner1;
    }
}

ScriptRunner::ScriptError ScriptRunner::startScript(uint deviceId)
{
  if (!deviceScriptList.contains(deviceId))
    {
      return ScriptRunner::ScriptEmpty;
    }
  else
    {
      ScriptRunner1 *scriptRunner1=deviceScriptList.value(deviceId);
      initScript(scriptRunner1);//初始化运行状态
      scriptRunner1->state_script = true;//设置脚本为开始状态
      runNextInstruction(scriptRunner1);//运行下一条指令
      return ScriptRunner::ScriptNoError;
    }
}

void ScriptRunner::stopScript(uint deviceId)//停止脚本，正常停止脚本，只停止下一步行动，重置各状态，不作出任何其余的处理，正常退出或者不需要继续脚本操作时使用
{
  if (!deviceScriptList.contains(deviceId))
    {
      return;
    }
  ScriptRunner1* scriptRunner1=deviceScriptList.value(deviceId);
  if (!scriptRunner1->state_script)
    {
      //如果已经停止了，则什么也不做
      return;
    }
  scriptRunner1->state_script = false;//设置脚本停止状态
  initScript(scriptRunner1);
}

bool ScriptRunner::getScriptState(uint deviceId)
{
  return deviceScriptList.value(deviceId)->state_script;
}

void ScriptRunner::handleTransmitResult(uint deviceId, uint resultType)
{
  (void)deviceId;
  (void)resultType;
}

void ScriptRunner::scriptWaitAndRun(int millisecond, uint deviceId) //等待millisecond毫秒后自动执行下个指令
{
  deviceScriptList.value(deviceId)->timer_wait.start(millisecond);
}

void ScriptRunner::sendInstruction(uint deviceId,uint type,float arg1,float arg2,float arg3)
{
  turnTableCodec->sendInstruction(deviceId,userId_scriptRunner,(TurnTableCodec::TurnTableInstructionFlag)type,arg1,arg2,arg3);
}

void ScriptRunner::slot_turnTableError(uint deviceId)//转台出错时停止脚本
{
  stopScript(deviceId);
}

void ScriptRunner::slot_timer_wait_timeOut(ScriptRunner1 *scriptRunner1)
{
  if(transmitCodec->isCanSendInstruction(scriptRunner1->deviceId))
    {
      //如果可以向设备发送数据
      scriptRunner1->timer_wait.stop();
      runNextInstruction(scriptRunner1);//执行下一条指令
    }
}

void ScriptRunner::slot_stateCheckerTimeOut(uint deviceId)
{
  stopScript(deviceId);
}

void ScriptRunner::slot_conditionReached(uint deviceId)
{
  scriptWaitAndRun(interval_instruction,deviceId);
}

void ScriptRunner::slot_targetDeviceMissed(uint deviceId)
{
  stopScript(deviceId);
}

void ScriptRunner::runNextInstruction(ScriptRunner1 *scriptRunner1)//运行下一条指令
{
  if (ifCanRun(scriptRunner1)) //寻找下一条可运行的指令，形式是各项栈的栈顶值，如果找不到下一条可运行的指令，则停止脚本的运行
    {
      emit signal_currentInstruction(scriptRunner1->deviceId, scriptRunner1->currentInstructionIndex);//发送当前设备和指令位置
      scriptRunner1->script.value(scriptRunner1->currentInstructionIndex)->run();//运行
      scriptRunner1->currentInstructionIndex++;//指令指针移动到下一个位置
    }
  else
    {
      stopScript(scriptRunner1->deviceId);//停止脚本
    }
}
void ScriptRunner::initScript(ScriptRunner1 *scriptRunner1)//重置脚本到初始位置
{
  scriptRunner1->timer_wait.stop();
  scriptRunner1->loopStack.clear();//循环栈清空
  scriptRunner1->loopCountStack.clear();//循环计数栈清空
  scriptRunner1->instructionCountStack.clear();//指令计数栈清空
  scriptRunner1->remainInstructionCountStack.clear();//剩余指令计数栈清空
}

void ScriptRunner::newLoop(uint deviceId, int loopCount, int instructionCount)//运行一个新的循环时，目标代码块和循环次数入栈
{
  ScriptRunner1* scriptRunner1=deviceScriptList.value(deviceId);
  scriptRunner1->loopStack.append(scriptRunner1->currentInstructionIndex);//循环栈入栈
  scriptRunner1->loopCountStack.append(loopCount);//循环计数栈入栈
  scriptRunner1->instructionCountStack.append(instructionCount);//指令计数栈入栈
  scriptRunner1->remainInstructionCountStack.append(instructionCount);//剩余指令计数栈入栈
}

void ScriptRunner::loopFinished(ScriptRunner1 *scriptRunner1)//循环层销毁，执行完毕的代码块出栈
{
  scriptRunner1->loopStack.removeLast();//循环栈出栈
  scriptRunner1->loopCountStack.removeLast();//循环计数栈出栈
  scriptRunner1->instructionCountStack.removeLast();//指令计数栈出栈
  scriptRunner1->remainInstructionCountStack.removeLast();//剩余指令计数栈出栈
}
bool ScriptRunner::ifCanRun(ScriptRunner1 *scriptRunner1) //检查是否可运行
{
  if(!scriptRunner1->state_script)
    {
      //如果脚本状态为停止，则不可运行
      return false;
    }
  if(scriptRunner1->loopStack.isEmpty())
    {
      //若循环栈为空
      if (scriptRunner1->script.size()<=scriptRunner1->currentInstructionIndex)
        {
          //若当前指令位置大于等于整个脚本的大小，且循环栈为空则说明代码运行完毕
          return false;//不可运行
        }
      else
        {
          //若当前指令位置小于整个脚本的大小，且循环栈为空，则说明处于根循环中
          return true;//可运行
        }
    }
  else
    {
      //若循环栈不为空
      if (scriptRunner1->remainInstructionCountStack.last()<=0)
        {
          //如果循环栈不为空且栈顶值小于等于0，意味着当前 循环已经运行完一次，开始执行检查循环次数的流程
          --scriptRunner1->loopCountStack.last();//循环计数栈栈顶自减一
          if (scriptRunner1->loopCountStack.last() > 0)
            {
              //如果循环计数栈栈顶减一后仍大于零，代表还有剩余循环次数需要执行
              //当前指令指向当前循环的第一条指令
              scriptRunner1->currentInstructionIndex=scriptRunner1->loopStack.last()+1;
              //使用当前循环的总指令数刷新剩余指令数
              scriptRunner1->remainInstructionCountStack.last()=scriptRunner1->instructionCountStack.last();
            }
          else//如果计数器自减一后小于等于零，代表循环次数运行完毕，如果刚开始的循环次数就等于零，那么该循环起码运行一次
            {
              loopFinished(scriptRunner1);//各栈执行出栈操作
            }
          return ifCanRun(scriptRunner1);//再执行一次本函数，判断脚本是否可以运行
        }
      else//如果剩余指令计数栈栈顶值大于零，意味着当前循环尚未执行完毕，继续执行
        {
          --scriptRunner1->remainInstructionCountStack.last();
          return true;//可运行
        }
    }
}

QList<ScriptInstruction*> ScriptRunner::getScript(uint deviceId)
{
  return deviceScriptList.contains(deviceId)?deviceScriptList.value(deviceId)->script:QList<ScriptInstruction*>();
}

bool ScriptRunner::setScript(uint deviceId,QList<ScriptInstruction*> script)
{
  if(deviceScriptList.contains(deviceId))
    {
      //如果存在已有的脚本
      if(deviceScriptList.value(deviceId)->state_script)
        {
          //如果该脚本正在运行，则设置新脚本失败
          return false;
        }
      else
        {
          //如果该脚本没有在运行，则删除该脚本
          deviceScriptList.take(deviceId)->deleteLater();
        }
    }
  //新建脚本
  ScriptRunner1* scriptRunner1 = new ScriptRunner1();
  scriptRunner1->deviceId = deviceId;
  scriptRunner1->script = script;
  //插入新的脚本
  deviceScriptList.insert(deviceId,scriptRunner1);
  connect(&scriptRunner1->timer_wait, &QTimer::timeout, [=](){slot_timer_wait_timeOut(scriptRunner1);});
  //设置新脚本成功
  return true;
}

int ScriptRunner::getInterval_instruction()
{
  return interval_instruction;
}
void ScriptRunner::setInterval_instruction(int interval_instruction)
{
  if (interval_instruction<1)
    {
      this->interval_instruction = 1;
    }
  else
    {
      this->interval_instruction = interval_instruction;
    }
}
int ScriptRunner::getTime_connectTimeOut()
{
  return interval_connectionTimeOut;
}
void ScriptRunner::setTime_connectTimeOut(int interval_connectionTimeOut)
{
  if (interval_connectionTimeOut<1)
    {
      this->interval_connectionTimeOut = 1;
    }
  else
    {
      this->interval_connectionTimeOut = interval_connectionTimeOut;
    }
}
int ScriptRunner::getInterval_turnTableOperationTimeOut()
{
  return interval_turnTableOperationTimeOut;
}
void ScriptRunner::setInterval_turnTableOperationTimeOut(int interval_turnTableOperationTimeOut)
{
  if (interval_turnTableOperationTimeOut<1)
    {
      this->interval_turnTableOperationTimeOut = 1;
    }
  else
    {
      this->interval_turnTableOperationTimeOut = interval_turnTableOperationTimeOut;
    }
}
TurnTableCodec* ScriptRunner::getTurnTableCodec()
{
  return turnTableCodec;
}
void ScriptRunner::setTurnTableCodec(TurnTableCodec* const turnTableCodec)
{
  if (Q_NULLPTR!=this->turnTableCodec)
    {
      disconnect(this->turnTableCodec, &TurnTableCodec::signal_turnTableError, this, &ScriptRunner::slot_turnTableError);
    }
  this->turnTableCodec = turnTableCodec;
  printInjection(this,turnTableCodec);
  if(Q_NULLPTR!=this->turnTableCodec)
    {
      connect(turnTableCodec, &TurnTableCodec::signal_turnTableError, this, &ScriptRunner::slot_turnTableError);
    }
}

RegisterCenter_Device* ScriptRunner::getRegisterCenter_Device()const
{
  return registerCenter_Device;
}

ScriptRunner* ScriptRunner::setRegisterCenter_Device(RegisterCenter_Device * const registerCenter_Device)
{
  this->registerCenter_Device=registerCenter_Device;
  printInjection(this,registerCenter_Device);
  return this;
}

TransmitCodec* ScriptRunner::getTransmitCodec()const
{
  return transmitCodec;
}

ScriptRunner* ScriptRunner::setTransmitCodec(TransmitCodec* const transmitCodec)
{
  this->transmitCodec=transmitCodec;
  printInjection(this,transmitCodec);
  return this;
}

void ScriptRunner::objectAutoInjection(QHash<QString,QObject*>* objMap)
{
  setTurnTableCodec(getObjFromMap<TurnTableCodec>(objMap));
  setRegisterCenter_Device(getObjFromMap<RegisterCenter_Device>(objMap));
  setTransmitCodec(getObjFromMap<TransmitCodec>(objMap));
}

ScriptRunner1::ScriptRunner1()
{}

ScriptRunner1::~ScriptRunner1()
{
  for(ScriptInstruction* scriptInstruction:script)
    {
      delete scriptInstruction;
    }
}
